# -*- coding: utf-8 -*-


import ast

from ir.ir_helper import *

from .config import *
from .ast_utils import convert_to_ast
from .convert_exp import convert_exp
from .convert_type import convert_ast_to_type


BUILT_IN_FILES = ['port', 'lane', 'channel', 'node']
BUILT_IN_HELPERS = {}
BUILT_IN_IGNORE = ['TopModule']


def convert_arguments(ast_args: ast.arguments, helper: Helper):
    func_arg = []
    # Parse the list argument first.
    for ast_arg in ast_args.args:
        # Ignore the self data.
        if ast_arg.arg == 'self' or ast_arg.arg == 'cls':
            continue
        # Save the argument names.
        arg_name = ast_arg.arg
        # Check the annotation of the arg.
        ast_type = convert_ast_to_type(ast_arg.annotation, helper)
        # Add the type and name to func arg list.
        func_arg.append([ast_type, arg_name, None])
    # Parse the default value.
    func_arg_default = []
    for ast_value in ast_args.defaults:
        # Convert the value.
        func_arg_default.append(convert_exp(ast_value, None, None))
    if len(func_arg_default) > 0:
        # Assign the value at the end of the argument list.
        default_start = len(func_arg) - len(func_arg_default)
        for ii in range(len(func_arg_default)):
            func_arg[default_start + ii][2] = func_arg_default[ii]
        # Show the result of function arguments.
    return func_arg


def create_class_helper(base: str, table: dict):
    # Create the helper.
    chelper = ClassHelper()
    chelper.base = base
    # Check whether base exist in define table.
    if chelper.base in table:
        # Copy the function and member.
        chelper.func = table[chelper.base].func.copy()
        chelper.member = table[chelper.base].member.copy()
    return chelper


def parse_builtin_file(filepath: str):
    # Get the file syntax tree.
    ast_module = convert_to_ast(filepath)
    # Create the classes helper dictionary.
    chelpers = {}
    # Fetch all the class definition from the file.
    for ast_line in ast_module.body:
        if not isinstance(ast_line, ast.ClassDef):
            continue
        # Get the class name.
        cls_name = ast_line.name
        if cls_name in BUILT_IN_IGNORE:
            # This class has been ignored.
            continue
        # Construct a helper.
        if len(ast_line.bases) > 0:
            base_type = ast_line.bases[0].id
        else:
            base_type = None
        class_helper = create_class_helper(base_type, chelpers)
        # Now check all the function definitions in the class.
        for ast_funcdef in ast_line.body:
            if not isinstance(ast_funcdef, ast.FunctionDef):
                continue
            # Ignore the private functions.
            if ast_funcdef.name.startswith('_'):
                continue
            # Convert the arguments.
            args = convert_arguments(ast_funcdef.args, None)
            # Convert the return types.
            ret_type = convert_ast_to_type(ast_funcdef.returns, None)
            # Add to classes helper.
            class_helper.func[ast_funcdef.name] = [ret_type, args]
        # Add class helper back to it.
        chelpers[cls_name] = class_helper
    return chelpers


def builtin_helper() -> dict:
    # If built-in helpers is already created.
    if len(BUILT_IN_HELPERS) > 0:
        return BUILT_IN_HELPERS
    # Add basic types for support.
    class_helper = ClassHelper()
    class_helper.base = ''
    class_helper.func = {
        'format': ['str', ['*']]
    }
    BUILT_IN_HELPERS['str'] = class_helper
    class_helper = ClassHelper()
    class_helper.base = ''
    class_helper.func = {
        'index': ['T', [['T', 'value', None]]]
    }
    BUILT_IN_HELPERS['List'] = class_helper
    # Loop for all the files in built-in list.
    for filename in BUILT_IN_FILES:
        # Generate the file path.
        filepath = os.path.abspath(os.path.join(HLM_LIB_PATH,
                                                HLM_LIB_NAME,
                                                '{}.py'.format(filename)))
        # Parse the builtin helper.
        BUILT_IN_HELPERS.update(parse_builtin_file(filepath))
    return BUILT_IN_HELPERS
